#include "DFA.h"
#include "DFAExceptions.h"

using namespace std;

//***********************************************
// RANGE

// -- Constructors --
DFA::Range::Range(string start, string end) : start(start), end(end) {
	if (end < start) {
		this->start = end;
		this->end = start;
	}
}

//-- Functions --
bool DFA::Range::operator< (const Range &other) const {
	return this->end < other.start;
}


//***********************************************
// State

//-- Constructors --
DFA::State::State(const string &accepting) : isAccepting(accepting == "Y"), defaultState("") {
	if (!isAccepting && accepting != "N") defaultState = accepting;
}

//-- Functions --

bool DFA::State::TransitionOn(DFA *dfa, string s) {
	//Put the given symbol into a range, for comparision
	Range key(s, s);
	//find the transition we need to take
	map<Range, string>::iterator transition = transitions.find(key);

	string stateName;
	//Didn't find a valid transition
	if (transition == transitions.end()) {
		if(isAccepting) return false;
		else if (!defaultState.empty()) stateName = defaultState;
		else throw TransitionUndefinedException();
	} else {
		stateName = transition->second;
	}

	map<string, State>::iterator nextState = dfa->_states.find(stateName);

	if (nextState == dfa->_states.end()) throw StateUndefinedException(); 

	dfa->_currentState = &(nextState->second);

	return true;
}


//***********************************************
// DFA

//-- Constructors --
DFA::DFA(const char *filename) : _startOver(false) {
	//Open the stream
	std::ifstream filestream(filename);
	if (filestream.fail()) throw DFAFileNotFound(); //BOOM

	//Start reading
	std::string stateName;

	//Couldn't find a single state
	if (!(filestream >> stateName)) throw EmptyDFAException();

	//Set the starting state to the first one in the file
	_currentState = (_startState = &_states[stateName]);
	do {
		//Get reference to the state so it can be updated
		string accepting;
		filestream >> accepting;

		State &s = (_states[stateName] = State( accepting ));
		s.name = &(_states.find(stateName)->first);

		//find the number of transitions this state has
		unsigned int numberOfTransitions;
		if (!(filestream >> numberOfTransitions)) throw NumberOfTransitionsUndefinedException();

		RemoveRestOfLine(filestream);

		std::string start, end, nextState;
		for (; numberOfTransitions > 0; --numberOfTransitions) {
			//Build the transition
			if (!( GetString(filestream, start) 
				&& GetString(filestream, end) 
				&& filestream >> nextState)) throw InvalidTransitionException();

			s.transitions[Range(start, end)] = nextState;

			RemoveRestOfLine(filestream);
		}
		//Get the next state
	} while (filestream >> stateName);

	filestream.close();
}

//-- Functions --
void DFA::RemoveRestOfLine(ifstream &stream) {
    string s;
	getline(stream, s);
}

bool DFA::GetString(ifstream &stream, string &s) {
	char c = stream.get();
	for (; c != '"' && c != '/'; c = stream.get());
	
	if (c == '"') return !(getline(stream, s, '"').eof());

	//The input is actually an ASCII value
	unsigned int i;
	stream >> i;
	c = (char)i;

	s.resize(1);
	s[0] = c;

    string s1;
	return !(getline(stream, s1, '/').eof());
}

void DFA::Reset() {
	_currentState = _startState;
	_startOver = false;
}

bool DFA::TransitionOn(const string s) {
	if (_startOver) Reset();

	//If the transition from this state to the next state exists return true
	if (_currentState->TransitionOn(this, s)) return true;
	else {
		_startOver = true;
		return false;
	}
}

std::string DFA::CurrentStateName() {
	return *_currentState->name;
}